/*
 * Copyright 2023 Google LLC
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
 * CRT library
 *
 * Utility functions written in assembly that can be used before the C
 * runtime has been initialized. These functions should not be used once
 * the C runtime has been initialized.
 *
 * The name of this library is historical. In many toolchains, a file called
 * "crt0.o" is linked into each executable, which does something similar to
 * what these functions
 */

  // NOTE: The "ax" flag below is necessary to ensure that this section
  // is allocated space in ROM by the linker.
  .section .crt, "ax", @progbits

  /**
   * Write zeros into the section bounded by the start and end pointers.
   * The section must be word (4 byte) aligned. It is valid for the section
   * to have a length of 0 (i.e. the start and end pointers may be equal).
   *
   * This function follows the standard ILP32 calling convention for arguments
   * but does not require a valid stack pointer, thread pointer or global
   * pointer.
   *
   * Clobbers a0 and t0.
   *
   * @param a0 pointer to start of section to clear (inclusive).
   * @param a1 pointer to end of section to clear (exclusive).
   */
  .balign 4
  .global crt_section_clear
  .type crt_section_clear, @function
crt_section_clear:

  // Check that start is before end.
  bgeu a0, a1, .L_clear_nothing

  // Check that start and end are word aligned.
  or   t0, a0, a1
  andi t0, t0, 0x3
  bnez t0, .L_clear_error

.L_clear_loop:
  // Write zero into section memory word-by-word.
  // TODO: unroll
  sw   zero, 0(a0)
  addi a0, a0, 4
  bltu a0, a1, .L_clear_loop
  ret

.L_clear_nothing:
  // If section length is 0 just return. Otherwise end is before start
  // which is invalid so trigger an error.
  bne a0, a1, .L_clear_error
  ret

.L_clear_error:
  ebreak

  // Set function size to allow disassembly.
  .size crt_section_clear, .-crt_section_clear

// -----------------------------------------------------------------------------

  /**
   * Copy data from the given source into the section bounded by the start and
   * end pointers. Both the section and the source must be word (4 byte) aligned.
   * It is valid for the section to have a length of 0 (i.e. the start and end
   * pointers may be equal). The source is assumed to have the same length as the
   * target section.
   *
   * The destination section and the source must not overlap.
   *
   * This function follows the standard ILP32 calling convention for arguments
   * but does not require a valid stack pointer, thread pointer or global
   * pointer.
   *
   * Clobbers a0, a2, t0 and t1.
   *
   * @param a0 pointer to start of destination section (inclusive).
   * @param a1 pointer to end of destination section (exclusive).
   * @param a2 pointer to source data (inclusive).
   */
  .balign 4
  .global crt_section_copy
  .type crt_section_copy, @function
crt_section_copy:

  // Check that start is before end.
  bgeu a0, a1, .L_copy_nothing

  // Check that start, end and src are word aligned.
  or   t0, a0, a1
  or   t0, t0, a2
  andi t0, t0, 0x3
  bnez t0, .L_copy_error

  // Check that source does not destructively overlap destination
  // (assuming a forwards copy).
  //
  // src  start          end
  //  |     |             |
  //  +-------------+     |
  //  |   source    |     |
  //  +-------------+     |
  //        +-------------+
  //        | destination |
  //        +-------------+
  //        |             |
  //      start          end
  //
  // TODO: disallow all overlap since it indicates API misuse?
  sub  t0, a0, a2           // (start - src) mod 2**32
  sub  t1, a1, a0           // end - start
  bltu t0, t1, .L_copy_error

.L_copy_loop:
  // Copy data from src into section word-by-word.
  // TODO: unroll
  lw   t0, 0(a2)
  addi a2, a2, 4
  sw   t0, 0(a0)
  addi a0, a0, 4
  bltu a0, a1, .L_copy_loop
  ret

.L_copy_nothing:
  // If section length is 0 just return. Otherwise end is before start
  // which is invalid so trigger an error.
  bne a0, a1, .L_copy_error
  ret

.L_copy_error:
  ebreak
  .size crt_section_copy, .-crt_section_copy
